/**
 * 
 */
package edu.berkeley.nlp.util;

import java.util.Arrays;

import edu.berkeley.nlp.math.DoubleArrays;
import edu.berkeley.nlp.math.SloppyMath;

/**
 * @author petrov
 *
 */
public class ScalingTools {
  // SCALING
	public static final int LOGSCALE = 100;
  public static final double SCALE = Math.exp(LOGSCALE);
  // Note: e^709 is the largest double java can handle.

  public static double calcScaleFactor(double logScale) {
  	return calcScaleFactor(logScale,SCALE);
  }
  
  public static double calcScaleFactor(double logScale, double scale) {
  	if (logScale==Integer.MIN_VALUE){
  		return 0.0;//System.out.println("give me a break!");
  	}
		if (logScale == 0.0)
			return 1.0;
		if (logScale == 1.0)
			return scale;
		if (logScale == 2.0)
			return scale * scale;
		if (logScale == 3.0)
			return scale * scale * scale;
		if (logScale == -1.0)
			return 1.0 / scale;
		if (logScale == -2.0)
			return 1.0 / scale / scale;
		if (logScale == -3.0)
			return 1.0 / scale / scale / scale;
		return Math.pow(scale, logScale);
	}
  
  public static int scaleArray(double[] scores, int previousScale){
  	if (previousScale==Integer.MIN_VALUE){
  		return previousScale;//System.out.println("give me a break!");
  	}
//  	if (true) return previousScale;
  	int logScale = 0;
	  double scale = 1.0;
	  double max = DoubleArrays.max(scores);
	  if (max==Double.POSITIVE_INFINITY) {
//	  	System.out.println("Infinity");
	  	return 0;
	  }
	  if (max==0) return previousScale;
	  while (max > SCALE) {
	    max /= SCALE;
	    scale *= SCALE;
	    logScale += 1;
	  }
	  while (max > 0.0 && max < 1.0 / SCALE) {
	    max *= SCALE;
	    scale /= SCALE;
	    logScale -= 1;
	  }
	  if (logScale != 0) {
	    for (int i = 0; i < scores.length; i++) {
	      scores[i] /= scale;
	    }
	  }
//	  if (SloppyMath.isDangerous(ArrayMath.max(scores))){
//	  	System.out.println("Undeflow when scaling scores!");
//	  	}
	  return previousScale + logScale;
  }
  

  public static void scaleArrayToScale(double[] scores, int previousScale, int newScale){
	  int scaleDiff = previousScale-newScale;
	  if (scaleDiff == 0) return; // nothing to do
	  double max = DoubleArrays.max(scores);
	  if (SloppyMath.isDangerous(max)) return;

	  double scale = calcScaleFactor(scaleDiff);
	  
	  if (Math.abs(scaleDiff)>=800){
	  	// under-/overflow...
	  	Arrays.fill(scores,0.0);
	  	return;
	  }

	  for (int i = 0; i < scores.length; i++) {
      scores[i] *= scale;
    }
//	  if (SloppyMath.isDangerous(ArrayMath.max(scores))){
//	  	System.out.println("Undeflow when scaling scores!");
//	  	}
  }

  public static double scaleToScale(double score, int previousScale, int newScale){
	  int scaleDiff = previousScale-newScale;
	  if (scaleDiff == 0) return score; // nothing to do
	  double max = score;
	  
	  if (SloppyMath.isDangerous(max)) return 0;

	  double scale = calcScaleFactor(scaleDiff);
	  
	  if (Math.abs(scaleDiff)>=800){
	  	// under-/overflow...
	  	return 0;
	  }

    score *= scale;
    
    return score;
  }

  public static boolean isBadScale(int scale)
	{
		return scale == Integer.MIN_VALUE || scale == Integer.MAX_VALUE || scale == Integer.MAX_VALUE - 1 || scale == Integer.MIN_VALUE + 1 ||  scale == Integer.MAX_VALUE - 2 || scale == Integer.MIN_VALUE + 2;
	}


  

}
